package osSRC.banker;

import osSRC.Format;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

class Banker {
	public int getProcessCount() {
		return processCount;
	}

	public int getResourceKindCount() {
		return resourceKindCount;
	}

	private int processCount;
	private int resourceKindCount;
	private int[][] max;
	private int[][] allocated;
	private int[] initAvailable;
	private int[][] available;
	Sequence sequence;
	public Banker(){}
	Banker(Banker banker){
		this.processCount=banker.getProcessCount();
		this.resourceKindCount=banker.getResourceKindCount();

		this.sequence=new Sequence(this.processCount);

		this.max=new int[this.processCount][this.resourceKindCount];
		this.allocated=new int[this.processCount][this.resourceKindCount];
		for(int i=0;i<this.processCount;i++){
			System.arraycopy(banker.max[i], 0, this.max[i], 0, this.resourceKindCount);
			System.arraycopy(banker.allocated[i], 0, this.allocated[i], 0, this.resourceKindCount);
		}

		this.initAvailable =new int[this.resourceKindCount];
		System.arraycopy(banker.initAvailable, 0, this.initAvailable, 0, this.resourceKindCount);

		this.available=new int[this.processCount][this.resourceKindCount];
	}
	Banker(String path){
		File file;
		Scanner scanner;
		try{
			file = new File(path);
			scanner=new Scanner(file);
			Main.open=true;
			this.resourceKindCount=scanner.nextInt();
			System.out.println(resourceKindCount+" kinds of resources");
			this.processCount=scanner.nextInt();
			System.out.println(processCount+" processes");
			this.sequence=new Sequence(this.processCount);
		//	this.sequence.findNextSequence(true);

			this.max=new int[this.processCount][this.resourceKindCount];
			this.allocated=new int[this.processCount][this.resourceKindCount];
			Format.titleExtend(true,"max",3);
			Format.titleExtend(true,"allocated",3);
			System.out.println();
			for(int i=0;i<this.processCount;i++){

				for(int j=0;j<this.resourceKindCount;j++)
					this.max[i][j]=scanner.nextInt();
				Format.outputArray(true,max[i]);
				for(int j=0;j<this.resourceKindCount;j++)
					this.allocated[i][j]=scanner.nextInt();
				Format.outputArray(true,allocated[i]);
				System.out.println();
			}

			this.initAvailable =new int[this.resourceKindCount];
			for(int j=0;j<this.resourceKindCount;j++)
				this.initAvailable[j]=scanner.nextInt();
			Format.titleExtend(true,"\nAvailable",3);
			Format.outputArray(true,this.initAvailable);
			this.available=null;
			System.out.println();
		}
		catch (FileNotFoundException fileNotFoundException){
			System.out.println("Cannot read file:"+path);
			System.exit(0);
		}
	}
	//void getSequence(){
	//	this.sequence.findNextSequence(false);
	//	this.sequence.show();
	//}
	void showBanker(int node,boolean satisfied,boolean title){
		int PID= sequence.getSequenceIndex(node);
		if(title){
			System.out.print("PID\t--\t");
			Format.titleExtend(false,"work", resourceKindCount);
			Format.titleExtend(true,"need--", resourceKindCount);
			Format.titleExtend(true,"max", resourceKindCount);
			Format.titleExtend(true,"allocated", resourceKindCount);
			if (satisfied)
				Format.titleExtend(true,"available",resourceKindCount);
			else System.out.println("N/A");
			System.out.println();
		}


		if(node==0 ){
			System.out.printf("%d\t--",PID);
			Format.outputArray(true,initAvailable);
		}
		else {
			Format.outputDig(PID);
			System.out.printf("(%d)\t",
					this.sequence.getSequenceIndex(node - 1));
			Format.outputArray(false,
					available[this.sequence.getSequenceIndex(node - 1)]);
		}
		System.out.print("\t");
		for(int i=0;i<this.resourceKindCount;i++)
			Format.outputDig(this.max[PID][i]-this.allocated[PID][i]);
		Format.outputArray(true,max[PID]);
		Format.outputArray(true,allocated[PID]);
		if(satisfied)Format.outputArray(true,available[PID]);
		System.out.println();
	}
	boolean acceptRequest(Request request){
		System.out.println("Trying to allocate resources...");
		Format.titleExtend(false,"PID",1);
		Format.titleExtend(true,"work",resourceKindCount);
		Format.titleExtend(true,"need--",resourceKindCount);
		Format.titleExtend(true,"available",resourceKindCount);
		System.out.println();
		Format.outputDig(request.PID);
		Format.outputArray(true,this.initAvailable);
		//judge whether req can be satisfied instantly
		for(int i=0;i<this.resourceKindCount;i++){
			int need=this.max[request.PID][i]
					-this.allocated[request.PID][i];
			int requestAmount=request.request[i];
			int availableAmount=this.initAvailable[i];
			if(requestAmount>need){
				System.out.println("no."+i+"request above need: dishonest request");
				return false;
			}
			if(requestAmount>availableAmount){
				System.out.println("available resources not enough for resource no."+i);
				return false;
			}
		}
		//satisfy
		for(int i=0;i<this.resourceKindCount;i++){
			this.allocated[request.PID][i]+=request.request[i];
			this.initAvailable[i]-=request.request[i];
		}
		//continue output
		System.out.print("\t");
		for(int i=0;i<this.resourceKindCount;i++){
			Format.outputDig(this.max[request.PID][i]
					-this.allocated[request.PID][i]);
		}
		Format.outputArray(true,this.initAvailable);
		System.out.println();
		for(int node=0;node<this.processCount;node++){
			this.showBanker(node,false,node==0);
		}
		System.out.println("Ready to judge system security");
		Format.pause();
		return true;
	}
	boolean safe(){
		int node;
		Scanner scanner=new Scanner(System.in);
		this.sequence.findNextSequence(true);
		for(int index=0;index<this.sequence.getLoopTimeLimit();index++){
			if(this.sequence.getSequenceIndex(0)==1)
				node=0;
			this.sequence.show();
			for (node=0;
				 node<this.processCount; node++){

				if(satisfiable(node)) {
					satisfy(node);
					this.showBanker(node,true,node==0);
				}
				else {
					this.showBanker(node,false,node==0);
					System.out.println("cannot be satisfied");
					System.out.println();
					break;
				}
			}
			if(node==this.processCount){
				System.out.println("Sequence Found");
				this.sequence.show();
			//	Format.pause();
				return true;
			}
			this.sequence.findNextSequence(false);
			System.out.flush();
		}
		return false;
	}
	boolean satisfiable(int nodeNum){
		int PID=this.sequence.getSequenceIndex(nodeNum);
//		if(false){
//			System.out.println(
//					"PID" + PID
//							+ ":\tNo." + nodeNum
//							+ " to be executed."
//			);
//			System.out.print("work\tneed\tallocated\tavailable\t");
//			System.out.println();
//		}
		if(nodeNum==0){
			for (int resourceID = 0; resourceID < this.resourceKindCount; resourceID++) {
				int need=this.max[PID][resourceID]-this.allocated[PID][resourceID];
				int available=this.initAvailable[resourceID];
//				if(false) {
//					System.out.printf("\t%d\t\t%d\t\t%d\n",
//							available, need,
//							this.allocated[PID][resourceID]);
//				}
				if (need> available) return false;
			}
			return true;
		}
		else {
			for (int resourceID = 0; resourceID < this.resourceKindCount; resourceID++) {
				int need=this.max[PID][resourceID] - this.allocated[PID][resourceID];
				int available=this.available[this.sequence.getSequenceIndex(nodeNum-1)][resourceID];
//				if(false)
//					System.out.printf("\t%d\t\t%d\t\t%d\n",
//						available,need,
//						this.allocated[PID][resourceID]);
				if (need> available)
					return false;
			}
			return true;
		}
	}
	void satisfy(int nodeNum){
		int PID=this.sequence.getSequenceIndex(nodeNum);
		for(int resourceID=0;resourceID<this.resourceKindCount;resourceID++){
			if(nodeNum==0){
				this.available[PID][resourceID]
						=this.initAvailable[resourceID]
						+this.allocated[PID][resourceID];
			}
			else {
				this.available[PID][resourceID]
						=this.available[this.sequence.getSequenceIndex(nodeNum-1)][resourceID]
						+this.allocated[PID][resourceID];
			}
		}
	}
}
